package cn.tannn.cat.file.server.service.impl;

import cn.tannn.cat.file.sdk.OssOperateAPI;
import cn.tannn.cat.file.sdk.api.UploadFile;
import cn.tannn.cat.file.sdk.api.UploadFiles;
import cn.tannn.cat.file.sdk.bean.FileIndex;
import cn.tannn.cat.file.sdk.bean.FileStorage;
import cn.tannn.cat.file.sdk.core.ftp.FtpOperate;
import cn.tannn.cat.file.sdk.core.local.LocalOperate;
import cn.tannn.cat.file.sdk.core.minio.MinioOperate;
import cn.tannn.cat.file.sdk.core.qiniu.QiNiuOperate;
import cn.tannn.cat.file.sdk.exception.FileException;
import cn.tannn.cat.file.server.dao.FileIndexMetaDao;
import cn.tannn.cat.file.server.dao.FileStorageConfigDao;
import cn.tannn.cat.file.server.entity.FileIndexMeta;
import cn.tannn.cat.file.server.entity.FileStorageConfig;
import cn.tannn.cat.file.server.service.FileOperateService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;

import static cn.tannn.cat.file.sdk.exception.FileExceptionCodeEnums.FILE_CONFIG_MASTER_NONENTITY;
import static cn.tannn.cat.file.sdk.exception.FileExceptionCodeEnums.FILE_CONFIG_NONSUPPORT;

/**
 * 文件操作实现
 *
 * @author <a href="https://tannn.cn/">tan</a>
 * @date 2024/3/15 14:04
 */
@Slf4j
@Service
public class FileOperateServiceImpl implements FileOperateService {
    private final FileIndexMetaDao fileIndexMetaDao;
    /**
     * 存储配置
     */
    private final FileStorageConfigDao fileStorageConfigDao;

    /**
     * 本地操作
     */
    private final OssOperateAPI localOperate;
    private final OssOperateAPI minioOperate;
    private final OssOperateAPI qiNiuOperate;
    private final FtpOperate ftpOperate;


    public FileOperateServiceImpl(FileStorageConfigDao fileStorageConfigDao,
                                  LocalOperate localOperate,
                                  FileIndexMetaDao fileIndexMetaDao,
                                  MinioOperate minioOperate,
                                  QiNiuOperate qiNiuOperate, FtpOperate ftpOperate
    ) {
        this.fileStorageConfigDao = fileStorageConfigDao;
        this.localOperate = localOperate;
        this.fileIndexMetaDao = fileIndexMetaDao;
        this.minioOperate = minioOperate;
        this.qiNiuOperate = qiNiuOperate;
        this.ftpOperate = ftpOperate;
    }

    @Override
    public FileIndexMeta upload(UploadFile upload) throws IOException {
        FileStorageConfig master = fileStorageConfigDao.findMaster()
                .orElseThrow(() -> FileException.specialMessage(FILE_CONFIG_MASTER_NONENTITY));
        return upload(upload, master);
    }

    @Override
    public FileIndexMeta upload(Long storageId, UploadFile upload) throws IOException {
        FileStorageConfig storageConfig = fileStorageConfigDao.findById(storageId)
                .orElseThrow(() -> FileException.specialMessage(FILE_CONFIG_NONSUPPORT));
        return upload(upload, storageConfig);
    }


    @Override
    public void uploads(UploadFiles uploads) {
        uploads.getFiles().forEach(files -> {
            try {
                UploadFile uploadFile = new UploadFile();
                uploadFile.setBucket(uploads.getBucket());
                uploadFile.setChildFolder(uploads.getChildFolder());
                uploadFile.setFile(files.getFile());
                uploadFile.setFileName(files.getFileName());
                upload(uploadFile);
            } catch (Exception e) {
                log.error("批量上传错误忽略", e);
            }
        });

    }

    @Override
    public void uploads(Long storageId, UploadFiles uploads) {
        FileStorageConfig storageConfig = fileStorageConfigDao.findById(storageId)
                .orElseThrow(() -> FileException.specialMessage(FILE_CONFIG_NONSUPPORT));
        uploads.getFiles().forEach(files -> {
            try {
                UploadFile uploadFile = new UploadFile();
                uploadFile.setBucket(uploads.getBucket());
                uploadFile.setChildFolder(uploads.getChildFolder());
                uploadFile.setFile(files.getFile());
                uploadFile.setFileName(files.getFileName());
                upload(uploadFile, storageConfig);
            } catch (Exception e) {
                log.error("批量上传错误忽略", e);
            }
        });
    }


    @Override
    public void download(Long fileIndexId, HttpServletResponse response) {
        fileIndexMetaDao.findById(fileIndexId)
                .ifPresent(file -> {
                            FileIndex fileIndex = file.toFileIndex();
                            if (fileIndex.localStorage()) {
                                localOperate.downloadFile(response, fileIndex);
                            } else if (fileIndex.minioStorage()) {
                                minioOperate.downloadFile(response, fileIndex);
                            } else if (fileIndex.qiNiuStorage()) {
                                qiNiuOperate.downloadFile(response, fileIndex);
                            } else if (fileIndex.ftpStorage()) {
                                ftpOperate.downloadFile(response, fileIndex);
                            }
                        }
                );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(Long findIndexId) {
        // 删除源文件/索引
        fileIndexMetaDao.findById(findIndexId)
                .ifPresent(this::remove);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(FileIndexMeta fileIndexMeta) {
        // 删除源文件
        boolean delete;
        FileIndex fileIndex = fileIndexMeta.toFileIndex();
        if (fileIndex.localStorage()) {
            // local
            delete = localOperate.removeFile(fileIndex);
        } else if (fileIndex.minioStorage()) {
            // minio
            delete = minioOperate.removeFile(fileIndex);
        } else if (fileIndex.qiNiuStorage()) {
            // 七牛
            delete = qiNiuOperate.removeFile(fileIndex);
        } else if (fileIndex.ftpStorage()) {
            // ftp
            delete = ftpOperate.removeFile(fileIndex);
        } else {
            delete = false;
        }
        // 删除索引记录
        if (delete) {
            fileIndexMetaDao.deleteById(fileIndex.getId());
        }
    }

    /**
     * 上传
     *
     * @param upload        文件信息
     * @param storageConfig 存储器信息
     * @return 文件索引
     * @throws IOException IOException
     */
    private FileIndexMeta upload(UploadFile upload, FileStorageConfig storageConfig) throws IOException {
        FileStorage storage = storageConfig.toFileStorage();
        FileIndex fileIndex;
        if (storage.localConfig()) {
            fileIndex = localOperate.uploadFile(upload, storage);
        } else if (storage.minioConfig()) {
            fileIndex = minioOperate.uploadFile(upload, storage);
        } else if (storage.qiNiuConfig()) {
            fileIndex = qiNiuOperate.uploadFile(upload, storage);
        } else if (storage.ftpConfig()) {
            fileIndex = ftpOperate.uploadFile(upload, storage);
        } else {
            throw FileException.specialMessage(11002, "暂不支持[" + storageConfig.getName() + "]存储");
        }
        //  判断重复，配置ID 和 path 重复的情况会清楚不在重复save
        FileIndexMeta fileIndexMeta = FileIndexMeta.toFileIndexMeta(fileIndex);
        if (!fileIndexMetaDao.existsByStorageIdAndUrlSuffix(storageConfig.getId(), fileIndex.getUrlSuffix())) {
            fileIndexMeta = fileIndexMetaDao.save(fileIndexMeta);
        }
        return fileIndexMeta;
    }

    @Override
    public String previewFile(Long fileIndexId, Integer expires) {
        return fileIndexMetaDao.findById(fileIndexId)
                .map(file -> {
                    FileIndex fileIndex = file.toFileIndex();
                    if (fileIndex.localStorage()) {
                        return localOperate.previewFile(fileIndex, expires);
                    } else if (fileIndex.minioStorage()) {
                        return minioOperate.previewFile(fileIndex, expires);
                    } else if (fileIndex.qiNiuStorage()) {
                        return qiNiuOperate.previewFile(fileIndex, expires);
                    } else if (fileIndex.ftpStorage()) {
                        return ftpOperate.previewFile(fileIndex, expires);
                    }
                    return null;
                })
                .orElseThrow(() -> new RuntimeException("文件不存在"));
    }

}
